//
//  FlickrClient.m
//  iFlick1.4
//
//  Created by vhly on 10-9-11.
//  Copyright 2010 xhzhang. All rights reserved.
//

#import "FlickrClient.h"
#import "MD5.h"
#import "DOM.h"
#import "Reachability.h"
#import "DataManager.h"
#import "UIImage+Extras.h"

static FlickrClient *flickrClientInstance;

@implementation FlickrClient

@synthesize userAccount;

+ (FlickrClient *)getInstance{
	if (flickrClientInstance == nil) {
		flickrClientInstance = [[FlickrClient alloc] initWithAPIKey:@"32fdd35bca5b784340c0bdf523b9d3cc" andSecret:@"fe8a457a3077648b"];
		[flickrClientInstance loadAuthInfo];
	}
	return flickrClientInstance;
}

- (id)initWithAPIKey:(NSString *)key andSecret:(NSString *)sec{
	if (self = [super init]) {
		apiKey = [key copy];
		apiSecret = [sec copy];
		images = [[NSMutableArray alloc] init];
	}
	return self;
}

#pragma mark -
#pragma mark initRESTCallInfo

/**
 * 创建默认请求参数
 * 默认包含 api_key
 * @return NSMutableDictionary
 */
- (NSMutableDictionary *)getInitParam{
	return [NSMutableDictionary dictionaryWithObject:apiKey forKey:@"api_key"];
}

- (NSString *)genAPISign:(NSDictionary *)params{
	NSMutableString *sb = [NSMutableString stringWithString:apiSecret];
	
	NSArray *akeys = [self getSortKeys:params];
	id value;	
	for(id key in akeys){
		value = [params objectForKey:key];
		// 主要针对部分非字符串的参数，比如图像上传时的图像二进制数据，这种不参与验证
		if ([value isKindOfClass:[NSString class]]) {
			[sb appendFormat:@"%@%@",key,[params objectForKey:key]];
		}
	}
	
	const char *us = [sb UTF8String];
	
	
	int len = strlen(us);
	MD5 m = MD5WithBytes(us,len);
	NSString * sret = MD5ToString(&m);
	return sret;	
}

- (NSArray *)getSortKeys:(NSDictionary *)params{
	NSArray * keys = [[params keyEnumerator] allObjects];
	
	NSArray *akeys = [keys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
	
	return akeys;
}

- (NSString *)getRESTRequest:(NSString *)endPoint method:(NSString *)method withParams:(NSMutableDictionary *)params{
	if (method != nil) {
		[params setObject:method forKey:@"method"];
	}
	NSString *sig = [self genAPISign:params];
	[params removeObjectForKey:@"method"];
	
	// TODO 此处检查一下method是否应该同样排列
	NSArray *sortedKey = [self getSortKeys:params];
	NSMutableString *sb = [NSMutableString stringWithString:endPoint];
	[sb appendString:@"?"];
	if (method) {
		[sb appendFormat:@"method=%@",method];
		if (params != nil && [params count] > 0) {
			[sb appendString:@"&"];
		}
	}
	int len = [sortedKey count];
	NSString *key;
	NSString *value;
	for (int i = 0; i < len; i ++) {
		key = (NSString *)[sortedKey objectAtIndex:i];
		value = (NSString *)[params objectForKey:key];
		[sb appendFormat:@"%@=%@&",[key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],[value stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
		//if (i < len - 1) {
//			[sb appendString:@"&"];
//		}
	}
	[sb appendFormat:@"api_sig=%@",sig];
	return sb;
}

#pragma mark -
#pragma mark Flickr Client Authorization

- (NSString *)getFrob{
	
	// flickr api is the same to other api , for example Twitter API, the api_sig alg is need param sort, and url is same to sig sort
	
	NSMutableDictionary *dict = [self getInitParam];
	// http://api.flickr.com/services/rest/?method=flickr.auth.getFrob&api_key=1a9abcbcaa7fba05955675c17c247fe4&auth_token=72157624928802336-4389c4dcc50f869d&api_sig=831ae7b373c8f1260d20fd7d239b4869
	// [dict setObject:@"flickr.auth.getFrob" forKey:@"method"];
	NSString *req = [self getRESTRequest:FLICKR_REST method:@"flickr.auth.getFrob" withParams:dict];
	NSString *xml = [self invokeRESTCall:req];
	NSLog(@"REST Result is = ",xml);
	/*
	 <rsp stat="ok">
	 <frob>72157624804216599-525936a5f456afd2-181586</frob>
	 </rsp>
	 
	 */
	
	NSString *frob = nil;
	DocumentBuilder *builder = [[DocumentBuilder alloc] init];
	Document *root = [[Document alloc] init];
	BOOL bok = [builder parseWithString:xml toResult:root];
	if(bok){
		Element *el = [root rootElement];
		NSString *stat = [el getAttribute:@"stat"];
		if(stat != nil && [stat isEqualToString:@"ok"]){
			NSArray *nds = [el getElementsByName:@"frob"];
			NSMutableString *sb = [NSMutableString string];
			if (nds != nil && [nds count] > 0) {
				el = (Element *)[nds objectAtIndex:0];
				nds = [el getChildNodes];
				for (Node *nd in nds) {
					if ([nd nodeType] == TEXT_NODE) {
						TextNode *tn = (TextNode *)nd;
						[sb appendString:tn.text];
					}
				}
				frob = sb;
				NSLog(@"Frob is %@",frob);
			}
		}
	}
	[root release];
	[builder release];
	return frob;
}


- (NSString *)getAuthorizeURL:(NSString *)frob{
	NSMutableDictionary *dict = [self getInitParam];
	[dict setObject:@"write" forKey:@"perms"];
	[dict setObject:frob forKey:@"frob"];
	return [self getRESTRequest:FLICKR_AUTH method:nil withParams:dict];
}


- (void)getToken:(NSString *)frob{
	NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
	[dict setObject:apiKey forKey:@"api_key"];
	[dict setObject:frob forKey:@"frob"];
	NSString *req = [self getRESTRequest:FLICKR_REST method:@"flickr.auth.getToken" withParams:dict];
	[dict release];
	NSString *xml = [self invokeRESTCall:req];
	if (xml) {
#ifdef DEBUG
		NSLog(@"Token is %@",xml);
#endif
		Document *dom = [[Document alloc] init];
		DocumentBuilder *builder = [[DocumentBuilder alloc] init];
		[builder parseWithString:xml toResult:dom];
		[builder release];
		Node *nd = [dom query:@"rsp/auth/token/#TEXT"];
		UserAccount *ret = [[UserAccount alloc] init];
		if(nd) {
			ret.auth_token = [nd getValue];
		}
		nd = [dom query:@"rsp/auth/perms/#TEXT"];
		if(nd) {
			ret.perms = [nd getValue];
		}
		nd = [dom query:@"rsp/auth/user"];
		if(nd) {
			ret.userName = [nd getAttribute:@"username"];
			ret.fullName = [nd getAttribute:@"fullname"];
			ret.nsid = [nd getAttribute:@"nsid"];
		}
		self.userAccount = ret;
		[ret release];
		[dom release];
	}
}

- (BOOL)isAuthorized{
	return self.userAccount != nil && self.userAccount.auth_token != nil;
}

- (void)logout{
	@synchronized(self){
		if (self.userAccount) {
			self.userAccount.auth_token = nil;
			self.userAccount.nsid = nil;
		}
	}
}

#pragma mark -
#pragma mark MakeAPICall


- (NSString *)invokeRESTCall:(NSString *)call{
	
	if ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] == NotReachable) {
		// TODO Not Reachable
		return nil;
	}
	
	NSString *ret = nil;
	if (call) {
		NSURL *url = [[NSURL alloc] initWithString:call];
		NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
		[req setHTTPMethod:@"GET"];
		NSString *version  = [[[NSBundle mainBundle] infoDictionary] valueForKey:[NSString stringWithFormat:@"CFBundleVersion"]];
		[req addValue:[NSString stringWithFormat:@"iFlick/%@",version] forHTTPHeaderField:@"User-Agent"];
		[req addValue:@"gzip" forHTTPHeaderField: @"Accept-Encoding"];
		NSURLResponse *resp = nil;
		NSError *err = nil;
		[self incNetworkRef];
		NSData *dt = [NSURLConnection sendSynchronousRequest:req returningResponse:&resp error:&err];
		[self decNetworkRef];
		[url release];
		[req release];
		if (err != nil) {
			// TODO Error
		} else if (resp != nil && dt != nil) {
			ret = [[[NSString alloc] initWithData:dt encoding:NSUTF8StringEncoding] autorelease];
		}

	}
	return ret;
}


- (void)incNetworkRef{
	networkRef++;
	if (networkRef == 1) {
		UIApplication *app = [UIApplication sharedApplication];
		app.networkActivityIndicatorVisible = YES;
	}
}

- (void)decNetworkRef{
	networkRef--;
	if (networkRef == 0) {
		UIApplication *app = [UIApplication sharedApplication];
		app.networkActivityIndicatorVisible = NO;
	}
}

#pragma mark -
#pragma mark SaveInfo

- (void)saveAuthInfo{
	[DataManager saveObjectToFile:self.userAccount withName:@"/userAuth.plist"];
}

- (BOOL)loadAuthInfo{
	BOOL bok = NO;
	id obj = [DataManager loadObjectFromFile:@"/userAuth.plist"];
	if (obj) {
		bok = YES;
		if ([obj isKindOfClass:[NSDictionary class]]) {
			// For old iFlick version, 1.3 or before
			NSDictionary *infos = (NSDictionary *)obj;
			UserAccount *ret = [[UserAccount alloc] init];
			ret.nsid = [infos objectForKey:@"nsid"];
			ret.auth_token = [infos objectForKey:@"token"];
			ret.userName = [infos objectForKey:@"username"];
			ret.fullName = [infos objectForKey:@"fullname"];
			ret.perms = [infos objectForKey:@"perms"];
			self.userAccount = ret;
			[ret release];
		}else if ([obj isKindOfClass:[UserAccount class]]) {
			// For new iFlick version, from 1.4
			self.userAccount = (UserAccount *)obj;
		}
	}
	return bok;
}

#pragma mark -
#pragma mark ImageUpload

- (NSData *)makeUploadRequest:(NSDictionary *)params{
	NSMutableData *ret = [[[NSMutableData alloc] init] autorelease];
	return ret;
}

#pragma mark -
#pragma mark ImageSupport

/**
 * 添加图像
 * @param libraryInfo 从UIImagePickerController中获取的图像信息
 */
- (void)addImage:(NSDictionary *)libraryInfo{
	if (libraryInfo) {
		UIImage *img = (UIImage *)[libraryInfo objectForKey:UIImagePickerControllerOriginalImage];
		
		// TODO Support Image Resolution 1600x1200 -> 320x480
		NSInteger it = [DataManager getConfigInteger:KEY_IMG_RESOLUTION];
		CGFloat iw = img.size.width;
		CGFloat ih = img.size.height;
		CGFloat sw,sh;
		if (it == KEY_IMG_RESOLUTION_320X480) {
			if (iw > 480 || ih > 480) {
				CGFloat ss;
				if (iw > ih) {
					ss = 480 / iw;
				}else {
					ss = 480 / ih;
				}
				sw = iw * ss;
				sh = ih * ss;
				
				// TODO small
				img = [img imageByScalingProportionallyToSize:CGSizeMake(sw, sh)];
			}
		}else if (it == KEY_IMG_RESOLUTION_LARGE) {
			// TODO Normal
		}
		
		
		NSDate *dt = [NSDate date];
		NSLocale *loc = [NSLocale currentLocale];
		// NSString *dm = [NSDateFormatter dateFormatFromTemplate:@"yMMMMd" options:0 locale:loc];
		NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
		[dateFormatter setLocale:loc];
		[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
		[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
		NSString *tt = [dateFormatter stringFromDate:dt];
#ifdef DEBUG
		NSLog(@"TT is %@",tt);
#endif
		[dateFormatter release];
		PhotoItem *pi = [[PhotoItem alloc] init];
		pi.description = tt;
		pi.title = @"Photo 1";
		pi.thumbImage = [img imageByScalingProportionallyToSize:CGSizeMake(44, 44)];
		pi.origImage = img;
		NSInteger sz = [images count];
		ItemStore *store;
		id obj = nil;
		for (int i = 0; i<sz; i++) {
			store = [images objectAtIndex:i];
			if ([tt isEqualToString:store.title]) {
				obj = store;
				break;
			}
		}
		if (obj == nil) {
			ItemStore *ma = [[ItemStore alloc] init];
			[ma addItem:pi];
			ma.title = tt;
			[images addObject:ma];
			[ma release];
		}else {
			ItemStore *ma = (ItemStore *)obj;
			[ma addItem:pi];
		}
		[pi release];
	}
}

- (NSInteger)imagesSize{
	return [images count];
}


- (ItemStore *)getItemStore:(NSInteger)index{
	ItemStore *ret = nil;
	NSInteger sz = [images count];
	if (index >= 0 && index < sz) {
		ret = (ItemStore *)[images objectAtIndex:index];
	}
	return ret;
}


#pragma mark -
#pragma mark MemoryManager

- (void)dealloc{
	[apiKey release];
	[apiSecret release];
	[userAccount release];
	[images release];
	[super dealloc];
}

@end
